import fileinput
import operator
class support_based(object):
    def __init__(self,tr_path,te_path, no_recom):
        self.dic = {}
        self.count=0
        self.average=0
        self.coverage=0
        self.recommendations=0
        self.correct=-1
        self.train_path=tr_path
        self.test_path=te_path
        self.no_recommendation=no_recom
    ''' 
    Builds and trains the model
    '''    
    def train(self):
        print "Training started"
        for line in fileinput.input([self.test_path]):
            try:
                line= line.strip().rstrip(',')
                line=line.split(',')
                length=len(line)-1
                if self.dic.has_key(length):
                    dic_tree=self.dic.get(length)
                    dic_tree.add(line)
                else:
                    tree=HybridTree()
                    tree.add(line)
                    self.dic[length]=tree
            except:
                continue
        print "Finished"
        
    def test(self):
        print "support_based"
    ''' 
    Trains the model continuously from partial sessions.
    ''' 
    def continous_train(self,line):
        line= line.strip().rstrip(',')
        line=line.split(',')
        length=len(line)-1
        if self.dic.has_key(length):
            dic_tree=self.dic.get(length)
            dic_tree.add(line)
        else:
            tree=HybridTree()
            tree.add(line)
            self.dic[length]=tree
    ''' 
    Make recommendations for partial sessions.
    '''     
    def recommend(self, session):
        templist=[]
        rtemplist=[]
        recommendationlist=[]
        result=[]
        self.recommendations+=1
        for r in reversed(session):
            templist.append(r)
            rtemplist=templist
            rtemplist.reverse()
            length=len(templist)
            if self.dic.has_key(length):
                dic_tree=self.dic.get(length)
                prediction = dic_tree.checkPath(rtemplist)
                if prediction:
                    prediction.sort(key=lambda x: x._count, reverse=True)
                    if len(prediction)>self.no_recommendation:
                        recommendationlist.append(prediction[0:self.no_recommendation])
                    else:
                        recommendationlist.append(prediction[0:])
        dictionary=self.recommenddomians(recommendationlist)
        if len(dictionary)>0:
            self.count+=1
            result=sorted(dictionary.items(), key=lambda x: x[1], reverse=True)
            result_list=self.predict_list(result)
        return result_list
    
    def predict_list(self,result):
        result_list=[]
        for element in result:
            result_list.append(element[0])
        return result_list
    
    def recommenddomians(self,recommendationlist):
        result={}
        for i in recommendationlist:
            if i:
                for j in i:
                    if result.has_key(j._item):
                        support = result.get(j._item)+j._count
                        result[j._item]=support
                    else:
                        result[j._item]=j._count
        return result            
    ''' 
    Evaluates the predictions made
    ''' 
    def evaluate(self,recommendationlist,actual):
        self.correct=0
        length =len(recommendationlist)
        for e in range(0,length):
            if recommendationlist[e]==actual:
                self.correct=1
                self.average = self.calculateAccuracy()
                return (self.average,self.count,self.correct)
        self.correct=0
        self.average=self.calculateAccuracy()
        return (self.average,self.count,self.correct) 
    
    def calculateAccuracy(self):
        return ((int(self.count)-1)*float(self.average)+int(self.correct))/int(self.count)
    
    def getCoverage(self):
        if not self.recommendations == 0:
            self.coverage=float(self.count)*100/float(self.recommendations)
        else:
            self.coverage=0
        return self.coverage
    
    def getAccuracy(self):
        return self.average*100
    
    def getCorrect(self):
        return self.correct
    def getCount(self):
        return self.count
    def run(self):
        self.train()
        print "Testing started"
        for line in fileinput.input([self.train_path]):
                   try:
                        list2=[]
                        line2= line.strip().rstrip(',')
                        line3=line2.split(',')
                        length=len(line3)
                        if length==1:
                            continue
                        for i in range(0,length-1):
                            patial_session=line3[0:i+1]
                            recomendation_list_aw=self.recommend(patial_session)
                            self.evaluate(recomendation_list_aw, line3[i+1])
                        self.continous_train(line)
                   except:
                        continue
        print "Accuracy of support_based : ",self.getAccuracy()
        print "Coverage of support_based : ",self.getCoverage()
        print "Count of support_based : ",self.getCount()
class HybridTree(object):
    def __init__(self):
        self._root = HybridNode(self, -1, None)

    @property
    def root(self):
        return self._root

    def add(self, transaction):
        point = self._root

        for item in transaction:
            next_point = point.search(item)
            if next_point:
                next_point.increment()
            else:
                next_point = HybridNode(self, item)
                point.add(next_point)
            point = next_point

    def printTree(self, root1):
        print "Root ", root1._item
        print root1._children.values()
        for x in root1._children.values():
            self.printTree(x)
            
    def checkPath(self,list1):
        list1.reverse()
        point = self._root
        for i in list1:
            node = point.search(i)
            if node:
                point=node
            else:
                return None
        return point._children.values()
               

class HybridNode(object):
    def __init__(self, tree, item, count=1):
        self._tree = tree
        self._item = item
        self._parent = None
        self._children = {}
        self._neighbor = None
        self._count = count

    def add(self, child):
        if not isinstance(child, HybridNode):
            raise TypeError("Can only add other Node as children")

        if not child.item in self._children:
            self._children[child.item] = child
            child.parent = self

    def search(self, item):
        try:
            return self._children[item]
        except KeyError:
            return None
            
    def __contains__(self, item):
        return item in self._children

    @property
    def tree(self):
        """The tree in which this node appears."""
        return self._tree

    @property
    def item(self):
        """The item contained in this node."""
        return self._item

    @property
    def count(self):
        """The count associated with this node's item."""
        return self._count

    def increment(self):
        """Increments the count associated with this node's item."""
        if self._count is None:
            raise ValueError("Root nodes have no associated count.")
        self._count += 1

    @property
    def root(self):
        """True if this node is the root of a tree; false if otherwise."""
        return self._item is None and self._count is None

    @property
    def leaf(self):
        """True if this node is a leaf in the tree; false if otherwise."""
        return len(self._children) == 0

    @property
    def children(self):
        """The nodes that are children of this node."""
#        print self._children.items()
#        return 1
        return tuple(self._children.itervalues())
#        return list(self._children.itervalues())

    def inspect(self, depth=0):
        print ('  ' * depth) + repr(self)
        for child in self.children:
            child.inspect(depth + 1)

    def __repr__(self):
        if self.root:
            return "<%s (root)>" % type(self).__name__
        return "<%s %r (%r)>" % (type(self).__name__, self.item, self.count)


if __name__ == "__main__": 
    aw=support_based('D:\\python\\DATSET\\3daysdata\\D\\train_d.csv','D:\\python\\DATSET\\3daysdata\\D\\test_d.csv',20)
    aw.run()
    